﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using SqlSugar;
using System;
using System.Linq;
using System.Reflection;

namespace JC_DirectBroadcast.Infrastructure.Extensions
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceProvider AddAutofacRegister(this IServiceCollection services,
            Action<ContainerBuilder> configurationAction = null,
            Action<IContainer> containerAction=null)
        {
            var builder=new ContainerBuilder();

            builder.Populate(services);
            configurationAction?.Invoke(builder);

            //进行服务注册
            //services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
            


            IContainer container = builder.Build();
            containerAction?.Invoke(container);

            return new AutofacServiceProvider(container);
        }


        /// <summary>
        ///     将给定的上下文作为服务注册在<see cref ="IServiceCollection"/>中
        ///     在应用程序中使用依赖注入时，如ASP.NET使用此方法。
        ///     有关设置依赖注入的更多信息，请参阅 http://go.microsoft.com/fwlink/?LinkId=526890。
        /// </summary>
        /// <example>
        ///     <code>
        ///          services.AddSQLSugarClient&lt;SqlSugarClient&gt;(config =>
        ///          {
        ///              config.ConnectionString = connectionString;
        ///              config.DbType = DbType.MySql;
        ///              config.IsAutoCloseConnection = true;
        ///              config.InitKeyType = InitKeyType.Attribute;
        ///          });
        ///      </code>
        /// </example>
        /// <typeparam name="TSugarClient"> 要注册的上下文的类型。 </typeparam>
        /// <param name="serviceCollection"> <see cref="IServiceCollection" /> 添加服务。 </param>
        /// <param name="configAction">
        ///     <para>
        ///         为上下文配置<see cref ="ConnectionConfig"/> 的操作。
        ///     </para>
        ///     <para>
        ///         为了将选项传递到上下文中，需要在您的上下文中公开构造函数
        ///         <see cref="ConnectionConfig" /> and passes it to the base constructor of <see cref="TSugarClient" />.
        ///     </para>
        /// </param>
        /// <param name="contextLifetime"> 用于在容器中注册TSugarClient服务的生命周期。 </param>
        /// <param name="configLifetime"> 用于在容器中注册ConnectionConfig服务的生命周期。 </param>
        /// <returns>
        ///     The same service collection so that multiple calls can be chained.
        /// </returns>
        public static IServiceCollection AddSQLSugarClient<TSugarClient>(
            [NotNull] this IServiceCollection serviceCollection,
            [CanBeNull] Action<ConnectionConfig> configAction = null,
            ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
            ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
            where TSugarClient : SqlSugarClient
            => AddSQLSugarClient<TSugarClient, TSugarClient>(serviceCollection, configAction, contextLifetime, optionsLifetime);

        public static IServiceCollection AddSQLSugarClient<TSugarClientService, TSugarClientImplementation>(
            [NotNull] this IServiceCollection serviceCollection,
            [CanBeNull] Action<ConnectionConfig> configAction = null,
            ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
            ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
            where TSugarClientImplementation : SqlSugarClient, TSugarClientService
            => AddSQLSugarClient<TSugarClientService, TSugarClientImplementation>(
                serviceCollection,
                configAction == null
                    ? (Action<IServiceProvider, ConnectionConfig>)null
                    : (p, b) => configAction.Invoke(b), contextLifetime, optionsLifetime);

        /// <summary>
        ///     <para>
        ///         将给定的上下文作为服务注册在<see cref ="IServiceCollection"/>中。
        ///         在应用程序中使用依赖注入时，如ASP.NET使用此方法。
        ///         有关设置依赖注入的更多信息，请参阅http://go.microsoft.com/fwlink/?LinkId=526890。
        ///     </para>
        /// </summary>
        /// <typeparam name="TSugarClientService"> 要注册的上下文的类型。 </typeparam>
        /// <param name="serviceCollection"> <see cref="IServiceCollection" /> 添加服务。 </param>
        /// <param name="configAction">
        ///     <para>
        ///         为上下文配置<see cref ="ConnectionConfig"/> 的操作。
        ///     </para>
        ///     <para>
        ///         为了将选项传递到上下文中，需要在您的上下文中公开构造函数
        ///         <see cref="ConnectionConfig" /> and passes it to the base constructor of <see cref="TSugarClient" />.
        ///     </para>
        /// </param>
        /// <param name="contextLifetime"> 用于在容器中注册TSugarClient服务的生命周期。 </param>
        /// <param name="configLifetime"> 用于在容器中注册ConnectionConfig服务的生命周期。 </param>
        /// <returns>
        ///     The same service collection so that multiple calls can be chained.
        /// </returns>
        public static IServiceCollection AddSQLSugarClient<TSugarClientService, TSugarClientImplementation>(
            [NotNull] this IServiceCollection serviceCollection,
            [CanBeNull] Action<IServiceProvider, ConnectionConfig> configAction,
            ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
            ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
            where TSugarClientImplementation : SqlSugarClient, TSugarClientService
        {
            Check.NotNull(serviceCollection, nameof(serviceCollection));

            if (contextLifetime == ServiceLifetime.Singleton)
            {
                optionsLifetime = ServiceLifetime.Singleton;
            }

            if (configAction != null)
            {
                CheckContextConstructors<TSugarClientImplementation>();
            }

            //AddCoreServices<TSugarClientImplementation>(serviceCollection, configAction, optionsLifetime);
            serviceCollection.TryAdd(new ServiceDescriptor(typeof(ConnectionConfig),
                p => ConnectionConfigFactory<TSugarClientImplementation>(p, configAction),
                optionsLifetime));
            serviceCollection.TryAdd(new ServiceDescriptor(typeof(TSugarClientService), typeof(TSugarClientImplementation), contextLifetime));

            return serviceCollection;
        }

        private static void AddCoreServices<TContextImplementation>(
            IServiceCollection serviceCollection,
            Action<IServiceProvider, ConnectionConfig> configAction,
            ServiceLifetime optionsLifetime)
            where TContextImplementation : SqlSugarClient
        {
            serviceCollection
                .AddMemoryCache()
                .AddLogging();

            serviceCollection.TryAdd(
                new ServiceDescriptor(
                    typeof(ConnectionConfig),
                    p => ConnectionConfigFactory<TContextImplementation>(p, configAction),
                    optionsLifetime));

            //为了不报错 不使用GetRequiredService方法
            //serviceCollection.Add(
            //    new ServiceDescriptor(
            //        typeof(ConnectionConfig),
            //        p => p.GetRequiredService<ConnectionConfig>(),
            //        //p => ConnectionConfigFactory(p, configAction),
            //        optionsLifetime));
        }

        private static ConnectionConfig ConnectionConfigFactory<TSugarClientService>(
            [NotNull] IServiceProvider applicationServiceProvider,
            [CanBeNull] Action<IServiceProvider, ConnectionConfig> configAction)
            where TSugarClientService : SqlSugarClient
        {
            var config = new ConnectionConfig();

            configAction?.Invoke(applicationServiceProvider, config);

            return config;
        }


        //检查 TSugarClient 构造函数是否有带参数的构造函数
        private static void CheckContextConstructors<TSugarClient>()
            where TSugarClient : SqlSugarClient
        {
            var declaredConstructors = IntrospectionExtensions.GetTypeInfo(typeof(TSugarClient)).DeclaredConstructors.ToList();
            if (declaredConstructors.Count == 1
                && declaredConstructors[0].GetParameters().Length == 0)
            {
                throw new ArgumentException(CoreStrings.SqlSugarClientMissingConstructor(typeof(TSugarClient).Name, typeof(ConnectionConfig).Name));
            }
        }
    }
}
